package com.wutong.resonance.model.plugin.visual;

import com.wutong.resonance.model.audio.AudioData;
import com.wutong.resonance.model.audio.AudioParams;
import com.wutong.resonance.model.plugin.Plugin;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.event.ActionEvent;
import javafx.scene.CacheHint;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.util.Duration;
import lombok.Getter;
import lombok.Setter;

import java.util.function.Function;

/**
 * 抽象的绘图插件
 *
 * @author wutong
 */
@Setter
@Getter
public abstract class VisualPlugin extends Plugin {

    protected Pane parent;
    protected Canvas canvas;
    protected GraphicsContext gc;

    protected Color backgroundColor = Color.BLACK;
    protected Color foregroundColor = Color.WHITE;

    protected Timeline timeline;
    protected AudioDataSupplier audioDataSupplier;

    /**
     * 初始帧大小
     */
    protected int initialFrameSize;
    /**
     * 帧大小
     */
    protected int frameSize;
    /**
     * 采样率
     */
    protected int sampleRate;
    /**
     * 窗口更新间隔 (毫秒)
     */
    protected double updateRate;

    /**
     * 每个样本在画布上的宽度
     */
    protected double xIncrement;
    /**
     * 中心线位置
     */
    protected double centerY;

    /**
     * 插件构造函数
     *
     * @param pluginName 插件名称
     * @param pluginDesc 插件描述
     */
    public VisualPlugin(String pluginName, String pluginDesc, Pane parent, GraphicsContext gc, AudioParams audioParams) {
        super(pluginName, pluginDesc, audioParams);
        this.parent = parent;
        this.gc = gc;
        this.canvas = gc.getCanvas();
        this.parent.getChildren().add(canvas);
    }

    public VisualPlugin(String pluginName, String pluginDesc, Pane parent, AudioParams audioParams) {
        super(pluginName, pluginDesc, audioParams);
        this.parent = parent;
        this.canvas = new Canvas();
        this.gc = canvas.getGraphicsContext2D();
        this.parent.getChildren().add(canvas);
    }

    @Override
    protected void initPlugin(AudioParams audioParams) {
        this.audioParams = audioParams;
        canvas.widthProperty().bind(parent.widthProperty());
        canvas.heightProperty().bind(parent.heightProperty());

        canvas.setCache(true);
        canvas.setCacheHint(CacheHint.SPEED);
    }

    protected void initTimeline() {
        initTimeline(e -> {
            AudioData audioData = (audioDataSupplier != null) ? audioDataSupplier.getAudioData() : null;
            if (audioData != null) {
                draw(audioData);
            }
            return null;
        });
    }

    protected void initTimeline(Function<ActionEvent, Void> action) {
        timeline = new Timeline(new KeyFrame(Duration.millis(updateRate), e -> {
            action.apply(e);
            // 获取最新的音频数据
            AudioData audioData = (audioDataSupplier != null) ? audioDataSupplier.getAudioData() : null;

            if (audioData != null) {
                draw(audioData);
            }
        }));
        timeline.setCycleCount(Timeline.INDEFINITE);
        timeline.play();
    }

    protected void initTimeline(ActionEvent actionEvent) {
        timeline = new Timeline(new KeyFrame(Duration.millis(updateRate), e -> actionEvent.consume()));
        timeline.setCycleCount(Timeline.INDEFINITE);
        timeline.play();
    }

    @Override
    protected void unloadPlugin() {
        canvas.widthProperty().unbind();
        canvas.heightProperty().unbind();
        canvas.getGraphicsContext2D().clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
    }

    @Override
    protected AudioData processPlugin(AudioData audioData) {
        draw(audioData);
        return audioData;
    }

    protected abstract void draw(AudioData audioData);

    // 添加一个接口，用于获取 AudioData
    public interface AudioDataSupplier {
        AudioData getAudioData();
    }

    protected void stop() {
        if (timeline != null) {
            timeline.stop();
        }
    }

    /**
     * 根据画布宽度动态调整 frameSize 如果 timeline 已经在运行，需要重新设置 KeyFrame 的 duration
     */
    protected void adjustFrameSize(double canvasWidth) {
        // 为了填满画布，frameSize 至少应该是画布宽度的两倍
        frameSize = Math.max(initialFrameSize, (int) (canvasWidth * 1.5));

        // 重新计算 updateRate
        updateRate = (double) frameSize / sampleRate * 1000;

        if (timeline != null) {
            timeline.stop();
            timeline.getKeyFrames().clear();
            timeline.getKeyFrames().add(new KeyFrame(Duration.millis(updateRate), e -> {
                AudioData audioData = (audioDataSupplier != null) ? audioDataSupplier.getAudioData() : null;
                if (audioData != null) {
                    draw(audioData);
                }
            }));
            timeline.play();
        }
    }

    protected void initDraw() {
        // 获取 Canvas 的宽度和高度
        double width = canvas.getWidth();
        double height = canvas.getHeight();

        // 清空画布
        gc.clearRect(0, 0, width, height);
        // 重新绘制背景
        redrawBackground();

        // 绘制波形
        // 波形颜色, 从父类获取
        gc.setStroke(foregroundColor);
        // 波形粗细
        gc.setLineWidth(1);

        // 每个样本在画布上的宽度
        xIncrement = width / frameSize;
        // 中心线位置
        centerY = height / 2;

        gc.beginPath();
    }

    /**
     * 重新绘制背景
     */
    protected abstract void redrawBackground();
}
